package nats_cli

import (
	"encoding/json"
	"fmt"
	"gluenet/modules/manager/server/adapter/nats-cli/nats"
	"gluenet/modules/manager/server/domain/dependency"
	"gluenet/modules/manager/server/domain/entity"
	"gluenet/modules/manager/server/infra"
	"gluenet/modules/manager/server/infra/prepare"
	"gluenet/pkg/agent/option"
	"gluenet/pkg/agent/types"
	v2 "gluenet/utils/rpc/v2/define"
	"gopkg.in/yaml.v3"
	"strings"
	"time"
)

func init() {
	prepare.RegisterRepo(func() {
		cli := NewInstanceCLI()
		prepare.InjectDependency(func() dependency.InstanceCLI {
			return cli
		})
	})
}

type Instance struct {
}

func (i Instance) StartObject(GUID string, Runtime interface{}) error {
	//nats.CLI
	return nil
}

func (i Instance) StopObject(GUID string, Runtime interface{}) error {
	//TODO implement me
	panic("implement me")
}

func (i Instance) CreateObject(obj *entity.Instance, agents interface{}) (interface{}, error) {
	switch obj.Platform {
	case entity.PlatformKubernetes:
		runtimeDetail := make(map[string]interface{})
		//specDetail := make(map[string]interface{})
		srv := obj.DetailRepoValue.(map[string]interface{})["platform_service"].(map[string]interface{})
		var withJaeger, withLinkerd = false, false
		if srv["jaeger"].(string) == "enable" {
			withJaeger = true
		}
		if srv["linkerd"] == "enable" {
			withJaeger = true
		}

		runTime, err := nats.CLI.Agent(agents.(string)).Releases(obj.GUID).Create(&types.Release{
			ChartName:   strings.Join([]string{"gluerepo", obj.Config.Name}, "/"),
			ReleaseName: obj.Name,
		}, option.CreateOptions{
			WithJaeger:  withJaeger,
			WithLinkerd: withLinkerd,
		})
		if err != nil {
			return nil, err
		}

		//specDetail["containers"] = runTime.Spec.Containers
		//specDetail["release"] = runTime.Spec.Release
		status := make(map[string]int, len(runTime.Spec.Containers))
		containers := make(map[string]interface{}, len(runTime.Spec.Containers))
		for _, v := range runTime.Spec.Containers {
			status[v] = infra.NotReady
			containers[v] = struct{}{}
		}
		fmt.Printf("\n\ncontainers: %v\n\n", runTime.Spec.Containers)
		//specDetail["state"] = status

		runtimeDetail["spec"] = runTime.Spec
		runtimeDetail["containers"] = containers
		runtimeDetail["state"] = status
		runtimeDetail["agents"] = agents
		runtimeDetail["platform"] = obj.Platform
		return runtimeDetail, nil
	case entity.PlatformGluenets:
		containers, err := getDetailValueContainers(obj.DetailRepoValue)
		if err != nil {
			return nil, err
		}

		agent_guids, ok := agents.(map[string]string)
		if !ok {
			return nil, fmt.Errorf("agents is not map[string][string]")
		}

		runtimeDetail := make(map[string]interface{})
		containerDetial := make(map[string]interface{})
		statusDetial := make(map[string]interface{})
		for _, container := range containers {
			bts, _ := yaml.Marshal(container)
			contain := types.Container{}
			if err = yaml.Unmarshal(bts, &contain); err != nil {
				return nil, fmt.Errorf("unmarshall %v", err)
			}

			containName := contain.Name

			guid, ok := agent_guids[containName]
			if !ok {
				return nil, fmt.Errorf("can't find containe %v agent %v", containName, guid)
			}
			contain.Name = strings.Join([]string{obj.GUID, containName}, "_")
			runTime, err := nats.CLI.Agent(guid).
				Containers(contain.Name).
				Create(&contain, option.CreateOptions{})

			if err != nil {
				return nil, err
			}
			containerDetial[containName] = runTime.Spec
			if runTime.Status.State == "Ready" {
				statusDetial[containName] = infra.Ready
			} else {
				statusDetial[containName] = infra.NotReady
			}
		}

		runtimeDetail["containers"] = containerDetial
		runtimeDetail["state"] = statusDetial
		runtimeDetail["agents"] = agent_guids
		runtimeDetail["platform"] = obj.Platform
		return runtimeDetail, nil
	}
	return nil, nil
}

func getDetailValueContainers(detailValue interface{}) ([]interface{}, error) {
	detail, ok := detailValue.(map[string]interface{})
	if !ok {
		return nil, fmt.Errorf("detail value not map[string]interface{}{}")
	}
	specs, ok := detail["spec"]
	if !ok {
		return nil, fmt.Errorf("detail value not contain spec")
	}
	spec, ok := specs.(map[string]interface{})
	if !ok {
		return nil, fmt.Errorf("detail value not map[string]interface{}{}")
	}
	containers, ok := spec["containers"]
	if !ok {
		return nil, fmt.Errorf("spec not contain containers")
	}
	cts, ok := containers.([]interface{})
	if !ok {
		return nil, fmt.Errorf("containers not map[string]interface{}{}")
	}
	return cts, nil
}

func (i Instance) RemoveObject(GUID string, Runtime interface{}) error {
	run, ok := Runtime.(map[string]interface{})
	if !ok {
		return fmt.Errorf("runtime is not map[string]interface{}")
	}
	agents, ok := run["agents"]
	if !ok {
		return fmt.Errorf("runtime can't find agents")
	}
	platform, ok := run["platform"]
	if !ok {
		return fmt.Errorf("runtime can't find platform")
	}
	switch platform {
	case entity.PlatformKubernetes:
		err := nats.CLI.Agent(agents.(string)).Releases(GUID).Delete(option.DeleteOptions{})
		if err != nil {
			return fmt.Errorf("delete %v", err)
		}
		return nil
	case entity.PlatformGluenets:
		agent_guidi, ok := agents.(map[string]interface{})
		if !ok {
			return fmt.Errorf("agents is not map[string]interface{}")
		}
		for contain, agent := range agent_guidi {
			err := nats.CLI.Agent(agent.(string)).Containers(strings.Join([]string{GUID, contain}, "_")).Delete(option.DeleteOptions{})
			if err != nil {
				return fmt.Errorf("delete %v", err)
			}
		}

		return nil
	}
	return fmt.Errorf("unknow platform")
}

func (i Instance) HttpProxy(agentGuid string, url string) (interface{}, error) {
	//fmt.Printf("url: %v", url)

	var req = v2.HttpGetProxyReq{
		HttpUrl: url,
	}

	var wapper = struct {
		Spec interface{} `json:"spec,omitempty"`
	}{req}

	bts, _ := json.Marshal(wapper)
	resp, err := nats.CLI.RPCClient().Nats().IRequest(agentGuid+":"+infra.RPCHttpProxyGet, bts, time.Minute)
	if err != nil {
		return nil, err
	}

	//fmt.Printf("\n\n %s \n\n", resp)
	var proxyResp = v2.HttpProxyResp{}
	err = json.Unmarshal(resp, &proxyResp)
	if err != nil {
		return nil, err
	}

	if proxyResp.IsSuccess {
		return fmt.Sprintf("%s", proxyResp.Data), nil
	}
	return nil, fmt.Errorf("req err %v", proxyResp.Message)
}

func (i Instance) InfoStrategy(strategy string, bestApp, guarantApp []string) (interface{}, error) {
	best := make(map[string]*entity.Instance, len(bestApp))
	guarant := make(map[string]*entity.Instance, len(guarantApp))

	for _, v := range bestApp {
		best[v] = &entity.Instance{}
	}

	for _, v := range guarantApp {
		guarant[v] = &entity.Instance{}
	}

	info := map[string]interface{}{
		"best_app":    best,
		"guarant_app": guarant,
	}
	bts, _ := json.Marshal(info)
	res, err := nats.CLI.RPCClient().Nats().IRequest(strategy+":instance.set", bts, time.Minute)
	if err != nil {
		return nil, err
	}
	return res, nil
}

func NewInstanceCLI() *Instance {
	r := &Instance{}
	return r
}
